tree grammar PalEval;

options {
    tokenVocab=PalExpr;
    ASTLabelType=CommonTree;
}

@header {
package procilege.antlr; 

import procilege.compiler.PalExprHelper;
import procilege.lang.BoolExprAnd;
import procilege.lang.BoolExprElement;
import procilege.lang.BoolExprOr;
import procilege.lang.EntityExprAnd;
import procilege.lang.EntityExprElement;
import procilege.lang.EntityExprOr;
import procilege.lang.ExprElement;
import procilege.lang.ExprSubstitute;
import procilege.lang.Name;
import procilege.lang.PredicateEntity;
import procilege.lang.PredicateEntityProperty;
import procilege.lang.PredicateExpr;
import procilege.lang.PredicateFunction;
import procilege.lang.PredicateObjectProperty;
import procilege.lang.PredicatePropertyExpr;
import procilege.lang.PredicateValue;
import procilege.lang.PrivExprElement;
import procilege.lang.RelationalExpr;
import procilege.lang.ScopeBlock;

import java.math.BigDecimal;
import com.google.inject.Inject;

}

@members {

@Inject
private PalExprHelper hlp;

}
    


prog[ScopeBlock scope]
    : trStatement[$scope]+
    ;

trStatement[ScopeBlock scope]
    : trPrivDefExpr[$scope] 
    | trLet[$scope]
    | trScopeBlock[$scope]
    ;

trScopeBlock[ScopeBlock scope]
    : ^(SCOPEBLOCK {$scope=$scope.spawn();} 
         trNamespaceExpr[$scope]+ trStatement[$scope]*)
    ;

trLet[ScopeBlock scope]
    : ^(LET_ASSIGN t=trPredicateProperty[$scope] v=trPredicteExp[$scope])
         { hlp.defAssigment($t.value, $v.value); }
    | ^(CATEGORY_SET a=trNameExpr[$scope] b=trNameExpr[$scope])
         { hlp.setEntityCategory(a, b); }
    | ^(CATEGORY_UNSET a=trNameExpr[$scope] b=trNameExpr[$scope])
         { hlp.unsetEntityCategory(a, b); }
    ;

trPrivDefExpr[ScopeBlock scope]
    : ^(PRIV_NAMEAS nm=trNameExpr[$scope] pv=trPrivExpr[$scope])
    	{ hlp.defPrivilege($nm.value, $pv.value); }
    ;

trPrivExpr[ScopeBlock scope] returns [PrivExprElement value]
    : ^(PRIV_COMPOSE pe1=trPrivExpr[$scope] pe2=trPrivExpr[$scope])
        { $value = hlp.compose($pe1.value, $pe2.value); }
    | ^(PRIV_MERGE   
           ( pe1=trPrivExpr[$scope] (
                  pe2=trPrivExpr[$scope]   
                   {$value = hlp.mergePrivilege($pe1.value, $pe2.value);}
                | ee2=trEntityExpr[$scope] 
                   {$value = hlp.mergeEntity($pe1.value, $ee2.value);}      
                | be2=trBoolExpr[$scope]   
                   {$value = hlp.mergeRestriction($pe1.value, $be2.value);}              
           ) | be1=trBoolExpr[$scope] { $value=hlp.mergeRestriction(null, $be1.value); } )
       )
    | ^(NAME_SUBSTITUTE pv=trPrivExpr[$scope] (subst=trSubstNamePairs[$scope, pv]))
         { value = subst; }
    | ^(PRIV_REF nm=trNameExpr[$scope]) { $value=hlp.privref($nm.value); }
    ;
    
trSubstNamePairs[ScopeBlock scope, ExprElement expr] returns [ExprSubstitute value]
@init{
    ExprElement objexpr = null;
    value = null;
}
    : (^(NAMEPAIR (onm=trNameExpr[$scope] {objexpr=onm;} | opv=trPrivExpr[$scope] {objexpr=opv;}) 
         oldn=trNameExpr[$scope])
         {
           if (value == null) 
              value = hlp.substitute(expr,  objexpr, oldn); 
           else
              value = hlp.substitute(value, objexpr, oldn);
         }
      )+
    ;
    
trEntityExpr[ScopeBlock scope] returns [EntityExprElement value]
    : ^(ENTITY_OR  args=trEntityExprList[$scope])
    	{ value = new EntityExprOr(args); }
    | ^(ENTITY_AND args=trEntityExprList[$scope]) 
        { value = new EntityExprAnd(args); }
    | ^(ENTITY_REF     nm=trNameExpr[$scope])
        { $value = hlp.entityname($nm.value); }
    ;    

trEntityExprList[ScopeBlock scope] returns [List<EntityExprElement> value]
@init { $value = new ArrayList<EntityExprElement>(); }
    :  (a=trEntityExpr[$scope] {$value.add($a.value);} )*
    ;
        
trBoolExpr[ScopeBlock scope] returns [BoolExprElement value]

    : ^(BOOL_OR  args=trBoolExprList[$scope])
    	{ value = new BoolExprOr(args); }
    | ^(BOOL_AND args=trBoolExprList[$scope]) 
        { value = new BoolExprAnd(args); }
    | ^(RELATION_EQ  a=trPredicteExp[$scope] b=trPredicteExp[$scope])
        { value = new RelationalExpr(RelationalExpr.RELATION_EQ, a, b); }
    | ^(RELATION_NEQ a=trPredicteExp[$scope] b=trPredicteExp[$scope])
        { value = new RelationalExpr(RelationalExpr.RELATION_NEQ, a, b); }
    | ^(RELATION_LE a=trPredicteExp[$scope] b=trPredicteExp[$scope])
        { value = new RelationalExpr(RelationalExpr.RELATION_LE, a, b); }
    | ^(RELATION_LT a=trPredicteExp[$scope] b=trPredicteExp[$scope])
        { value = new RelationalExpr(RelationalExpr.RELATION_LT, a, b); }
    | ^(RELATION_GE a=trPredicteExp[$scope] b=trPredicteExp[$scope])
        { value = new RelationalExpr(RelationalExpr.RELATION_GE, a, b); }
    | ^(RELATION_GT a=trPredicteExp[$scope] b=trPredicteExp[$scope])
        { value = new RelationalExpr(RelationalExpr.RELATION_GT, a, b); }
    | ^(RELATION_CONFORM lnm=trNameExpr[$scope] rpv=trPrivExpr[$scope])
        { value = hlp.conform(lnm, rpv); }
    | ^(RELATION_CATEGORY a=trPredicteExp[$scope] b=trPredicteExp[$scope])
        { value = new RelationalExpr(RelationalExpr.RELATION_CATEGORY, a, b); }
    ;

    
trBoolExprList[ScopeBlock scope] returns [List<BoolExprElement> value]
@init { $value = new ArrayList<BoolExprElement>(); }
    :  (a=trBoolExpr[$scope] {$value.add($a.value);} )*
    |  p=trPredicteExp[$scope] {$value.add($p.value);}
    ;
    


trPredicteExp[ScopeBlock scope] returns [PredicateExpr value]
    : ^(PREDICATE_FUNC  n=trNameExpr[$scope] args=trPredicteExpList[$scope])
    	{ $value = new PredicateFunction($n.value, $args.value); } 
    | ^(PREDICATE_NAME  n=trNameExpr[$scope])
	{ $value = new PredicateEntity($n.value); } 
    | ^(PREDICATE_VALUE s=trStringValue)
        { $value = new PredicateValue($s.value); }
    | ^(PREDICATE_VALUE d=DECIMAL_LITERAL)
        { $value = new PredicateValue(new BigDecimal($d.text)); }
    | prop=trPredicateProperty[$scope] {$value = $prop.value; }
    ;
    
trPredicateProperty[ScopeBlock scope] returns[PredicatePropertyExpr value]
    : ^(PREDICATE_PROPERTY na=trNameExpr[$scope] id=IDENTIFIER)
        { $value = new PredicateEntityProperty($na.value, $id.text, $scope); }
    | ^(PREDICATE_PROPERTY pp=trPredicateProperty[$scope] id=IDENTIFIER)
        { $value = new PredicateObjectProperty($pp.value, $id.text, $scope); }
    ; 

   
      
trPredicteExpList[ScopeBlock scope] returns [List<PredicateExpr> value]
@init {
$value = new ArrayList<PredicateExpr>();
}
    : (a=trPredicteExp[$scope] {$value.add($a.value);} )+
    ;
    
trNamespaceExpr[ScopeBlock scope]
    :^(NAMESPACE ns=trStringValue (px=IDENTIFIER)?)
         {$scope.registerNamespace($ns.value, ($px == null)? null : $px.text);}
    ;

trNameExpr[ScopeBlock scope] returns [Name value]	
    : ^(NAME lp=IDENTIFIER (px=IDENTIFIER)? ) 
         {$value=$scope.name(($px == null)? null : $px.text, $lp.text); }
    ;
    
trNameExprSeq[ScopeBlock scope] returns [List<Name> value]
@init {
$value = new ArrayList<Name>();
}
    :  (a=trNameExpr[$scope] {$value.add($a.value);} )+
    ;
       	    
trStringValue returns [String value]
    : s=STRING_LITERAL { $value = $s.text.substring(1, $s.text.length()-1); }
    ;
